using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Common;

namespace SqlPrompt
{
	class QueryProcessor
	{
		public string PreviousQuery = "";

		DbConnectionInfo dbConnectionInfo;
		IDbConnection conn = null;
		
		public void Connect(DbConnectionInfo dbConnectionInfo)
		{
			try
			{
				Disconnect();

				this.dbConnectionInfo = dbConnectionInfo;

				conn = DbFactory.CreateDbConnection(dbConnectionInfo);
				conn.Open();
				App.Msg("Connected to '" + dbConnectionInfo.Name + "' successfully.");
			}
			catch (Exception e)
			{
				App.Error("Connect error: " + e.Message);
			}
		}

		public void Disconnect()
		{
			try
			{
				if (conn != null && conn.State != ConnectionState.Closed)
				{
					conn.Close();
					App.Msg("Disconnected from '" + dbConnectionInfo.Name + "' successfull.");
				}
			}
			catch (Exception e)
			{
				App.Error("Disconnect error: " + e.Message);
			}
		}

		public void ProcessSqlCommand(string cmd, IDataTarget dataTarget, bool schemaOnly)
		{
			try
			{
				if (conn == null || conn.State == ConnectionState.Closed)
				{
					App.Error("No data connection exists.");
					return;
				}

				long startTicks = DateTime.Now.Ticks;

				IDbCommand dbCmd = DbFactory.CreateDbCommand(dbConnectionInfo);
				dbCmd.Connection = conn;
				dbCmd.CommandText = cmd;

				CommandBehavior commandBehaviour = schemaOnly ?
					CommandBehavior.SchemaOnly : CommandBehavior.KeyInfo;

				using (IDataReader reader = dbCmd.ExecuteReader(commandBehaviour))
				{
					int rowsProcessed = 0;

					DataTable dtSchema = reader.GetSchemaTable();
					if (dtSchema != null)
					{
						DataColumn[] columns = DataColumn.BuildFromSchemaTable(dtSchema);

						if (schemaOnly)
							DisplaySchema(columns);
						else
						{
							rowsProcessed = ProcessQuery(dataTarget, columns, reader);
							App.Msg("Processed " + rowsProcessed + " rows.");
						}
					}

					QueryHistory.LogQuery(dbConnectionInfo, cmd, rowsProcessed);
				}

				long endTicks = DateTime.Now.Ticks;
		
				App.Msg("Query executed successfully in " + DateUtil.GetTimeSpanStringFromTicks(endTicks - startTicks) + ".");

				PreviousQuery = cmd;
			}
			catch (DbException e)
			{
				App.Error("SQL Error: " + e.Message);
			}
			catch (Exception e)
			{
				App.Error("General Error: " + e.Message);
			}
		}

		void DisplaySchema(DataColumn[] columns)
		{
			int longestNameLength = 4;
			foreach (DataColumn column in columns)
				if (column.Name.Length > longestNameLength)
					longestNameLength = column.Name.Length;

			App.ColumnHeading("Index", 5, true);
			App.ColumnHeading("Name", longestNameLength, false);
			App.ColumnHeading("Type", 15, false);
			App.ColumnHeading("Size", 10, false);
			App.ColumnHeading("Is Long", 7, false);
			App.NewLine();

			Console.ForegroundColor = ConsoleColor.Gray;
			for (int i = 0; i < columns.Length; i++)
			{
				DataColumn column = columns[i];
				Console.Write((i + 1).ToString().PadRight(6));
				Console.Write(column.Name.PadRight(longestNameLength + 1));
				Console.Write(column.Type.Name.PadRight(16));
				Console.Write(column.Size.ToString().PadRight(11));
				Console.Write(column.IsLong.ToString().PadRight(7));
				App.NewLine();
			}
		}

		int ProcessQuery(IDataTarget dataTarget, DataColumn[] columns, IDataReader reader)
		{
			try
			{
				dataTarget.Init(columns);

				int currentChunkSize = 0;
				int rowsProcessed = 0;
				while (true)
				{
					bool moreData = reader.Read();
					bool abortQuery = false;

					if (dataTarget.ChunkSize > 0 && (dataTarget.ChunkSize == currentChunkSize || !moreData))
					{
						dataTarget.FlushChunk();
						
						if (moreData)
						{
							App.NewLine();
							if (!App.YesNo("Continue?"))
								abortQuery = true;

							App.NewLine();
						}

						currentChunkSize = 0;
					}

					if (!moreData) break;

					if (abortQuery || CheckAbortQuery())
					{
						App.Msg("Query aborted!");
						break;
					}
					
					dataTarget.WriteRow(reader);
					rowsProcessed++;
					currentChunkSize++;
				}

				return rowsProcessed;
			}
			finally
			{
				dataTarget.Finish();
			}
		}

		bool CheckAbortQuery()
		{
			if (!Console.KeyAvailable) return false;

			ConsoleKeyInfo key = Console.ReadKey(true);
			if (key == null || key.Key != ConsoleKey.Escape) return false;

			return true;
		}
	}
}
